package jp.syuriken.snsw.terenaibot;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.MissingResourceException;
import java.util.Random;
import java.util.logging.Level;

import jp.syuriken.snsw.terenaibot.internal.Clocker;
import jp.syuriken.snsw.terenaibot.internal.PluginContainerImpl;
import jp.syuriken.snsw.terenaibot.internal.StdoutPostWrapper;
import jp.syuriken.snsw.terenaibot.internal.ThreadHolder;
import jp.syuriken.snsw.terenaibot.internal.TwitterPostWrapper;
import jp.syuriken.snsw.utils.Logger;
import jp.syuriken.snsw.utils.MyResourceBundle;

/**
 * TerenaiBotSystem コアクラス。
 * 
 * @author $Author: snswinhaiku.lo@gmail.com $
 * @version $Rev: 18 $
 */
public class BotSystem extends Thread {
	
	/** ランダムポストデータファイル */
	private static final String RANDOM_POSTDATA_FILE = "data/randpost.txt";
	
	/** デフォルトの設定ファイル */
	private static final String DEFAULT_CONFIG_FILE = "tbotsys.cfg";
	
	private static final int MSEC_TO_SEC = 1000;
	
	private ThreadHolder clockHolder;
	
	private Clocker clocker;
	
	private volatile boolean isFinish = false;
	
	private long wakeCount = 0;
	
	private int randomPostSpan;
	
	private String[] randomPostData;
	
	private Random random;
	
	private BotConfiguration botConfiguration;
	
	private PluginContainer pluginContainer;
	

	/**
	 * インスタンスを生成する。
	 * @param logger ロガー
	 * @throws FileNotFoundException ファイルが見つかりません。
	 * @throws IOException ロガーファイルが開けません。
	 * @throws ConfigurationException 設定ファイルに問題があります。
	 * @see #BotSystem(Logger,File)
	 */
	public BotSystem(Logger logger) throws IOException, ConfigurationException {
		this(logger, new File(DEFAULT_CONFIG_FILE));
	}
	
	/**
	 * インスタンスを生成する。
	 * @param logger ロガー
	 * @param configFile 設定ファイル。
	 * @throws FileNotFoundException ファイルが見つかりません
	 * @throws ConfigurationException 設定ファイルに問題があります。
	 * @throws IOException ロガーファイルが開けません。
	 * @see #BotSystem(Logger)
	 */
	public BotSystem(Logger logger, File configFile) throws ConfigurationException, IOException {
		super("tbotsys-main"); // スレッド名の指定
		
		botConfiguration = new BotConfiguration();
		botConfiguration.setConfigurationFile(configFile);
		botConfiguration.setLogger(logger);
		logger.log(Level.INFO, "Initilaizing...");
		
		MyResourceBundle resourceBundle = new MyResourceBundle(botConfiguration.getConfigurationFile());
		String postTo = getStringFromConfiguration(resourceBundle, "core.postto");
		PostWrapperPool.addWrapper("twitter", TwitterPostWrapper.class);
		PostWrapperPool.addWrapper("stdout", StdoutPostWrapper.class);
		
		pluginContainer = new PluginContainerImpl(botConfiguration, "plugin/");
		
		PostWrapper postWrapper = null;
		try {
			postWrapper = PostWrapperPool.getWrapper(postTo, botConfiguration);
		} catch (InstantiationException e) {
			logger.log(Level.SEVERE, e, "PostWrapperを初期化できませんでした");
		} catch (IllegalAccessException e) {
			logger.log(Level.SEVERE, e, "PostWrapperを初期化できませんでした");
		}
		
		if (postWrapper == null) {
			throw new ConfigurationException(botConfiguration.getConfigurationFile(), "core.postto",
					ConfigurationExceptionType.ILLEGAL);
		}
		botConfiguration.setWrapper(postWrapper);
		
		clockHolder = new ThreadHolder();
		random = new Random();
		clocker = new Clocker(clockHolder, getLongFromConfiguration(resourceBundle, "clock.interval") * MSEC_TO_SEC);
		randomPostSpan = getIntFromConfiguration(resourceBundle, "post.random.span");
		loadRandomPost();
		
		logger.log(Level.CONFIG, " PostWrapper: " + postWrapper.getWrapperName());
		try {
			logger.log(Level.CONFIG, " User: " + postWrapper.getUserName());
		} catch (IllegalStateException e) {
			logger.log(Level.SEVERE, "PostWrapperでエラー: " + e.getLocalizedMessage());
			System.err.println("Wrapperのエラーです: IDなどが間違ってないか確認してください。");
			e.printStackTrace();
		} catch (WrapperException e) {
			logger.log(Level.WARNING, "ユーザー名の取得でエラーが発生しました。");
			logger.log(Level.FINE, e.getCause());
		}
		pluginContainer.constructPlugins(botConfiguration);
		logger.log(Level.INFO, "Initilaized.");
	}
	
	private int getIntFromConfiguration(MyResourceBundle resourceBundle, String configName)
			throws ConfigurationException {
		try {
			return Integer.parseInt(resourceBundle.getString(configName));
		} catch (NumberFormatException e) {
			throw new ConfigurationException(botConfiguration.getConfigurationFile(), configName,
					ConfigurationExceptionType.NOT_DECIMAL);
		}
	}
	
	private long getLongFromConfiguration(MyResourceBundle resourceBundle, String configName)
			throws ConfigurationException {
		try {
			return Long.parseLong(resourceBundle.getString(configName));
		} catch (NumberFormatException e) {
			throw new ConfigurationException(botConfiguration.getConfigurationFile(), configName,
					ConfigurationExceptionType.NOT_DECIMAL);
		}
	}
	
	private String getStringFromConfiguration(MyResourceBundle resourceBundle, String configName)
			throws ConfigurationException {
		try {
			return resourceBundle.getString(configName);
		} catch (MissingResourceException e) {
			throw new ConfigurationException(botConfiguration.getConfigurationFile(), configName,
					ConfigurationExceptionType.NOT_FOUND);
		}
	}
	
	private void loadRandomPost() {
		ArrayList<String> randomPostDataList = new ArrayList<String>();
		BufferedReader reader = null;
		try {
			reader = new BufferedReader(new FileReader(RANDOM_POSTDATA_FILE));
			while (true) {
				String line = reader.readLine();
				if (line == null) {
					break;
				} else if (line.startsWith("#") || line.trim().isEmpty()) {
					continue;
				}
				
				randomPostDataList.add(line);
			}
		} catch (FileNotFoundException e) {
			System.err.println(RANDOM_POSTDATA_FILE + " が見つかりませんでした。");
		} catch (IOException e) {
			System.err.println(e.getLocalizedMessage());
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e) {
					System.err.println(e.getLocalizedMessage());
				}
			}
		}
		randomPostData = randomPostDataList.toArray(new String[randomPostDataList.size()]);
	}
	
	/**
	 * ボットのアクションを選択する
	 */
	private void nextBotAction() {
		if (wakeCount % randomPostSpan == 0) {
			postByRandom();
		}
	}
	
	/**
	 * ランダムにポストを行う
	 */
	private void postByRandom() {
		if (randomPostData.length != 0) {
			String postString = randomPostData[random.nextInt(randomPostData.length)];
			botConfiguration.getWrapper().post(postString);
		}
	}
	
	@Override
	public void run() {
		clocker.start();
		while (!isFinish) {
			nextBotAction();
			wakeCount++;
			try {
				synchronized (clockHolder) {
					if (!isFinish) {
						clockHolder.wait();
					}
				}
			} catch (InterruptedException e) {
				// do nothing
			}
		}
		clocker.stopClocker();
		try {
			clocker.join();
		} catch (InterruptedException e) {
			// do nothing
		}
	}
	
	/**
	 * ボットを停止する。実際に停止したかどうかは{@link #join()}などを使ってください。
	 */
	public void stopBot() {
		isFinish = true;
		clocker.stopClocker();
	}
}
